<?php

/**
 *
 * @author lovebing<lovebing365@126.com> Aug. 30, 2014
 */
class Wechat {

    const API_COMMON_BASE_URL = 'https://api.weixin.qq.com/cgi-bin';
    const COMMON_BASE_URL = 'https://mp.weixin.qq.com/cgi-bin';
    const API_MEDIA_BASE_URL = 'http://file.api.weixin.qq.com/cgi-bin/media';
    const WEIXIN_FLAG = 'weixinredirect';
    const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';
    const OAUTH_AUTHORIZE_URL = '/authorize?';
    const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀
    const OAUTH_TOKEN_URL = '/sns/oauth2/access_token?';
    const OAUTH_USERINFO_URL = '/sns/userinfo?';
    
    /**
     * 
     * @param type $appid
     * @param type $secret
     * @return type
     */
    public static function getAccessToken($appid, $secret,$isneedtoken=null) {
        $path = '/token';
        $params = array(
            'grant_type' => 'client_credential',
            'appid' => $appid,
            'secret' => $secret
        );

        $url = self::API_COMMON_BASE_URL . $path;
        $http = HTTPClient::getInstance();
        $res = json_decode($http->get($url, $params));
        if (isset($res->errcode)) {
            throw new Exception("error:".$res->errmsg, $res->errcode);
        }
  
        $data = array(
            'access_token' => $res->access_token,
            'expires_in' => $res->expires_in
        );
        
        $model = Pubs::findModelByAppidAndSec($appid,$secret);
        if ($model) {

            $model->access_token = $data['access_token'];
            $model->access_token_expiry_time = date('Y-m-d H:i:s', strtotime('+' . ($data['expires_in']-1800) . ' seconds'));

            $model->save();
        }
                
        if($isneedtoken){
            return $res->access_token ; 
        }

        return $data;
    }
    
    public static function sendMsgByOpenId($comId,$openId,$msg){
    	WechatConf::loadSetting2($comId); 
    	$appid = WechatConf::getPayAppId(); 
    	$secret = WechatConf::getPaySecretKey(); 
    	$accessToken = self::getAccessToken($appid, $secret,1); 
    	self::sendTxtMsg($accessToken, $openId, $msg); 
    }
    
    
    public static function sendTxtMsg($accessToken,$touser,$text){
    	$path = "/message/custom/send?access_token=".$accessToken;
    	$url = self::API_COMMON_BASE_URL.$path ; 
		$postData ='{"touser":"'.$touser.'","msgtype":"text","text":{"content":"'.$text.'"}}';
	
    	$http = HTTPClient::getInstance();
    	return $http->postText($url, $postData);
    }

    /**
     * 
     * @param type $accessToken
     * @param type $touser
     * @param type $templateId
     * @param string $gotourl
     * @param type $topcolor
     * @param array $data
     * @return type
     */
    public static function sendTemplateMessage($accessToken, $touser = '', $templateId = '', $gotourl = '', $topcolor = '#FF0000', array $data = array()) {
        $path = '/message/template/send?access_token=' . $accessToken;
        $url = self::API_COMMON_BASE_URL . $path;
        $postData = array(
            'touser' => $touser,
            'template_id' => $templateId,
            'url' => $gotourl,
            'topcolor' => $topcolor,
            'data' => $data
        );
        $content = json_encode($postData);
        $http = HTTPClient::getInstance();
        return $http->postText($url, $content);
    }

    /**
     * 
     * @param string $appid
     * @param string $redirectUri
     */
    public static function getWechatOauthCode($appid, $redirectUri = __SELF__) {
        $url = 'https://open.weixin.qq.com/connect/oauth2/authorize';
        $hash = '#wechat_redirect';
        $params = array(
            'appid' => $appid,
            'redirect_uri' => $redirectUri,
            'response_type' => 'code',
            'scope' => 'snsapi_base',
            'state' => self::WEIXIN_FLAG
        );
        $url = $url . '?' . http_build_query($params) . $hash;
        header('Location: ' . $url);
    }

    /**
     * 
     * @param string $appid
     * @param string $secret
     * @param string $code
     * @param int $errcode
     * @param string $errmsg
     * @return boolean
     */
    public static function getWechatOpenid($appid, $secret, $code, &$errcode = 0, &$errmsg = '') {
        $grant_type = 'authorization_code';
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
        $params = array(
            'appid' => $appid,
            'secret' => $secret,
            'code' => $code,
            'grant_type' => $grant_type
        );
        $http = HTTPClient::getInstance();
        $res = $http->get($url, $params);
        $data = json_decode($res, true);
        if (empty($data) || !isset($data['openid'])) {
            $errcode = isset($data['errcode']) ? $data['errcode'] : 1;
            $errmsg = isset($data['errmsg']) ? $data['errmsg'] : '';
            return false;
        }
        $errcode = 0;
        $errmsg = '';
        return $data['openid'];
    }

    /*
     * 通过openid获取微信用户的基本信息，比如昵称和头像
     */

    public static function getWXUserInfoByOpenid($appid, $secret, $openid) {
        //1.通过$appid,$secret获取accesstoken
        $accessToken = self::getAccessToken($appid, $secret);
        $path = '/user/info';
        $params = array(
            'access_token' => $accessToken['access_token'],
            'openid' => $openid
        );

        $url = self::API_COMMON_BASE_URL . $path;
        $http = HTTPClient::getInstance();
        $res = json_decode($http->get($url, $params));
        if (isset($res->errcode)) {
            throw new Exception($res->errmsg, $res->errcode);
        }
        if(isset($res->nickname))
        {
            $data = array(
                'nickname' =>preg_replace('~\xEE[\x80-\xBF][\x80-\xBF]|\xEF[\x81-\x83][\x80-\xBF]~', '', $res->nickname),  //Validation::replace_specialChar($res->nickname),
                'headimgurl' => $res->headimgurl
            );
        }
        else
        {
            $data = array(
                'nickname' => '',
                'headimgurl' => ''
            );
        }

        return $data;
    }
    
    /**
     * oauth 授权跳转接口
     * @param string $callback 回调URI
     * @return string
     */
    public static function getOauthRedirect($callback, $appid, $state = 'STATE', $scope = 'snsapi_userinfo') {
        return self::OAUTH_PREFIX . self::OAUTH_AUTHORIZE_URL . 'appid=' . $appid . '&redirect_uri=' . urlencode($callback) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect';
    }

    /**
     * 通过code获取Access Token
     * @return array {access_token,expires_in,refresh_token,openid,scope}
     */
    public static function getOauthAccessToken($appid,$appsecret) {
        $code = isset($_GET['code']) ? $_GET['code'] : '';
        if (!$code)
            return false;

        $result = self::http_get(self::API_BASE_URL_PREFIX . self::OAUTH_TOKEN_URL . 'appid=' . $appid . '&secret=' . $appsecret . '&code=' . $code . '&grant_type=authorization_code');

        if ($result) {
            $json = json_decode($result, true);
//            if (!$json || !empty($json['errcode'])) {
//                return false;
//            }
            //$this->user_token = $json['access_token'];
            return $json;
        }
        return false;
    }
    
    /**
     * 获取授权后的用户资料
     * @param string $access_token
     * @param string $openid
     * @return array {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]}
     * 注意：unionid字段 只有在用户将公众号绑定到微信开放平台账号后，才会出现。建议调用前用isset()检测一下
     */
    public static function getOauthUserinfo($access_token, $openid) {
        $result = self::http_get(self::API_BASE_URL_PREFIX . self::OAUTH_USERINFO_URL . 'access_token=' . $access_token . '&openid=' . $openid);
        if ($result) {
            $json = json_decode($result, true);
//            if (!$json || !empty($json['errcode'])) {
//                return false;
//            }
            return $json;
        }
        return false;
    }
    
    /**
     * GET 请求
     * @param string $url
     */
    private static function http_get($url) {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return false;
        }
    }

    public static function createEditAddressJsParams($appId, $accessToken, $url) {
        $params = array(
            'appId' => $appId,
            'timeStamp' => time() . '',
            'nonceStr' => uniqid()
        );
        $signData = $params;
        $signData['accessToken'] = $accessToken;
        $signData['url'] = $url;

        $params['scope'] = 'jsapi_address';
        $params['signType'] = 'SHA1';
        $params['addrSign'] = self::createEditAddressSign($signData);

        return $params;
    }

    /**
     * 
     * @param array $params
     * @return type
     */
    public static function createEditAddressSign(array $params) {
        ksort($params);
        $str = array();
        foreach ($params as $k => $v) {
            if ($v == '') {
                continue;
            }
            $str[] = strtolower($k) . '=' . $v;
        }
        return sha1(implode('&', $str));
    }

    /**
     * 
     * @param array $params
     * @param type $key
     * @return type
     */
    public static function createPaySign(array $params, $key) {
        ksort($params);
        $str = array();
        foreach ($params as $k => $v) {
            if ($v == '') {
                continue;
            }
            $str[] = $k . '=' . $v;
        }
        $stringSignTemp = implode('&', $str) . '&key=' . $key;
        $signValue = strtoupper(md5($stringSignTemp));
        return $signValue;
    }

    /**
     * 
     * @param type $accessToken
     * @param type $sceneId
     * @param boolean $enableShorUrl 默认false
     * @return type
     * @throws Exception
     */
    public static function getQrcodeUrl($accessToken, $sceneId, $enableShorUrl = false) {
        $data = self::getQrcode($accessToken, $sceneId);
        if ($enableShorUrl) {
            return self::getShortUrl($accessToken, $data->url);
        }
        return $data->url;
    }

    /**
     * 
     * @param type $accessToken
     * @param type $sceneId
     * @param type $sceneStr
     * @param string $type QR_LIMIT_SCENE or QR_SCENE (临时)
     * @param int  $expireSeconds
     * @return type
     * @throws Exception
     */
    public static function getQrcode($accessToken, $sceneId, $sceneStr = '', $type = 'QR_LIMIT_SCENE', $expireSeconds = 1800) {
        $url = self::API_COMMON_BASE_URL . '/qrcode/create?access_token=' . $accessToken;
        $params = array(
            'action_name' => $type,
            'action_info' => array(
                'scene' => array(
                    'scene_id' => $sceneId
                )
            )
        );

        if ($type == 'QR_SCENE') {
            $params['expire_seconds'] = $expireSeconds;
        }
        $http = HTTPClient::getInstance();
        $text = $http->postText($url, json_encode($params));
        $data = json_decode($text);
        if (empty($data)) {
            throw new Exception('Error:' . $text);
        }
        if (!isset($data->url)) {
            throw new Exception('Error:' .$data->errmsg.';token:'.$accessToken, $data->errcode);
        }
        return $data;
    }

    /**
     * 
     * @param type $accessToken
     * @param type $longUrl
     * @return type
     * @throws Exception
     */
    public static function getShortUrl($accessToken, $longUrl) {
        $url = self::API_COMMON_BASE_URL . '/shorturl?access_token=' . $accessToken;
        $params = array(
            'action' => 'long2short',
            'long_url' => $longUrl
        );

        $http = HTTPClient::getInstance();
        $text = $http->postText($url, json_encode($params));
        $data = json_decode($text);
        if (empty($data)) {
            return $longUrl;
            //throw new Exception('Error:' . $text);
        }
        if (!isset($data->short_url)) {
            return $longUrl;
            //throw new Exception($data->errmsg, $data->errcode);
        }
        return $data->short_url;
    }

    public static function getQrcodeImageUrl($ticket) {
        return self::COMMON_BASE_URL . '/showqrcode?ticket=' . $ticket;
    }

    /**
     * 
     * @param type $appid
     * @param type $secret
     * @return type
     */
    public static function getJsApiTicket($accessToken) {

        $url = self::API_COMMON_BASE_URL . "/ticket/getticket?type=jsapi&access_token=$accessToken";
        $http = HTTPClient::getInstance();
        $res = json_decode($http->get($url));

        if (isset($res->errcode) && $res->errcode != "0") {
            if(strpos($res->errmsg,'access_token is invalid or not latest'))
            {
                return "access_token is invalid or not latest";
            }
            throw new Exception($res->errmsg, $res->errcode);
        }

        $data = array(
            'jsapi_ticket' => $res->ticket,
            'expires_in' => $res->expires_in
        );

        return $data;
    }

    public static function createJsApiParams($appId, $url, $jsapiTicket) {
        $params = array(
            'appId' => $appId,
            'timestamp' => time(),
            'nonceStr' => md5(microtime(true)),
        );
        $signParams = array(
            'noncestr' => $params['nonceStr'],
            'jsapi_ticket' => $jsapiTicket,
            'timestamp' => $params['timestamp'],
            'url' => $url
        );
        $params['signature'] = self::createJsApiSign($signParams);
        $params['jsApiList'] = array(
            'onMenuShareTimeline',
            'onMenuShareAppMessage',
            'onMenuShareQQ',
            'onMenuShareWeibo',
            'startRecord',
            'stopRecord',
            'onVoiceRecordEnd',
            'playVoice',
            'pauseVoice',
            'stopVoice',
            'onVoicePlayEnd',
            'uploadVoice',
            'downloadVoice',
            'chooseImage',
            'previewImage',
            'uploadImage',
            'downloadImage',
            'translateVoice',
            'getNetworkType',
            'openLocation',
            'getLocation',
            'hideOptionMenu',
            'showOptionMenu',
            'hideMenuItems',
            'showMenuItems',
            'hideAllNonBaseMenuItem',
            'showAllNonBaseMenuItem',
            'closeWindow',
            'scanQRCode',
            'chooseWXPay',
            'openProductSpecificView',
            'addCard',
            'chooseCard',
            'openCard',
        );
        return $params;
    }

    /**
     * 
     * @param array $params
     * @return type
     */
    public static function createJsApiSign(array $params) {
        ksort($params);
        $str = array();
        foreach ($params as $k => $v) {
            if ($v == '') {
                continue;
            }
            $str[] = $k . '=' . $v;
        }
        $stringSignTemp = implode('&', $str);
        $signValue = sha1($stringSignTemp);
        return $signValue;
    }

    /**
     * 
     * @param type $accessToken
     * @param type $mediaId
     * @return type
     * @throws RuntimeException
     */
    public static function getMedia($accessToken, $mediaId, &$fileName = '') {
        $url = self::API_MEDIA_BASE_URL . '/get';
        $params = array(
            'access_token' => $accessToken,
            'media_id' => $mediaId
        );
        $http = new HTTPClient();
        $res = $http->get($url, $params);
        $disposition = $http->getResponseHeaderItem('Content-disposition');
        if (empty($disposition)) {
            $data = json_decode($res, true);
            throw new RuntimeException($data['errcode'] . ': 获取文件失败' . $data['errmsg'], $data['errcode']);
        }
        $fileName = HTTPClient::getFileNameFromDisposition($disposition[0]);

        return $res;
    }

    /**
     * 
     * @param type $accessToken
     * @param type $path
     * @param type $type
     * @return type
     * @throws RuntimeException
     */
    public static function uploadMedia($accessToken, $path, $type = 'image') {
        $url = self::API_MEDIA_BASE_URL . '/upload?access_token=' . $accessToken . '&type=' . $type;

        $res = HttpClient::uploadFile($url, $path);
        $data = json_decode($res, true);

        if (!isset($data['media_id'])) {
            throw new RuntimeException($data['errcode'] . ': 上传文件失败' . $data['errmsg'], $data['errcode']);
        }
        return $data['media_id'];
    }

    /**
     * 
     * @param type $text
     * @param type $from
     * @param type $to
     */
    public static function responseText($text, $from, $to) {
        $data = array(
            'ToUserName' => $to,
            'FromUserName' => $from,
            'CreateTime' => time(),
            'MsgType' => 'text',
            'Content' => $text,
            'FuncFlag' => 0
        );
        header('Content-Type: text/xml; charset=utf-8');
        echo XML::arrayToXml($data);
    }

    /**
     * 
     * @param type $mediaid
     * @param type $from
     * @param type $to
     */
    public static function responseImage($mediaid, $from, $to) {
        $data = array(
            'ToUserName' => $to,
            'FromUserName' => $from,
            'CreateTime' => time(),
            'MsgType' => 'image',
            'Image' => array(
                'MediaId' => $mediaid
            )
        );
        header('Content-Type: text/xml; charset=utf-8');
        echo XML::arrayToXml($data);
    }

    /**
     * 
     * @param type $accessToken
     * @param type $openid
     * @return type
     */
    public static function getUserInfo($accessToken, $openid) {
        $url = self::API_COMMON_BASE_URL . '/user/info';
        $parmas = array(
            'access_token' => $accessToken,
            'openid' => $openid
        );
        
        $res = HTTPClient::getInstance()->get($url, $parmas);
        $data = json_decode($res, true);
        if($data['errcode'] != 0) {
             throw new RuntimeException($data['errcode'] . ': 获取用户信息失败' . $data['errmsg'], $data['errcode']);
        }
        if(isset($data['nickname']))
        {
            $data['nickname']=preg_replace('~\xEE[\x80-\xBF][\x80-\xBF]|\xEF[\x81-\x83][\x80-\xBF]~', '', $data['nickname']);
            $data['nickname']=trim(Validation::replace_specialChar($data['nickname']));
        }
        
        return $data;
    }
    

    /*
     * 通过openid获取微信用户是否关注公众号状态值
    */
    
    public static function getSubscribeByOpenid($appid, $secret, $openid) {
    	//1.通过$appid,$secret获取accesstoken
    	$accessToken = self::getAccessToken($appid, $secret);
    	$path = '/user/info';
    	$params = array(
    			'access_token' => $accessToken['access_token'],
    			'openid' => $openid
    	);
    
    	$url = self::API_COMMON_BASE_URL . $path;
    	$http = HTTPClient::getInstance();
    	$res = json_decode($http->get($url, $params));
    	if (isset($res->errcode)) {
    		throw new Exception($res->errmsg, $res->errcode);
    	}
    	if(isset($res->subscribe))
    	{
    		$data = array(
    				'subscribe' => $res->subscribe
    		);
    	}
    	else
    	{
    		$data = array(
    				'subscribe' => ''
    		);
    	}
    
    	return $data;
    }

}
